package oa.hleast.xswl.service.venue.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import oa.hleast.common.result.Result;
import oa.hleast.common.utils.DateUtils;
import oa.hleast.common.web.exception.BizException;
import oa.hleast.common.web.util.RequestUtils;
import oa.hleast.xswl.common.config.AppProps;
import oa.hleast.xswl.common.constant.ReviewStatusEnum;
import oa.hleast.xswl.pojo.domain.CmsVenueMain;
import oa.hleast.xswl.pojo.domain.CmsVenueSignIn;
import oa.hleast.xswl.pojo.dto.ManagerUserDTO;
import oa.hleast.xswl.pojo.dto.UmsUserDto;
import oa.hleast.xswl.pojo.form.CmsVenueSignInForm;
import oa.hleast.xswl.pojo.form.SendNoticeForm;
import oa.hleast.xswl.pojo.form.VenueScheduleAddForm;
import oa.hleast.xswl.pojo.vo.*;
import oa.hleast.xswl.service.IUmsUserService;
import oa.hleast.xswl.service.venue.CmsVenueMainService;
import oa.hleast.xswl.service.venue.CmsVenueScheduleService;
import oa.hleast.xswl.service.venue.CmsVenueSignInService;
import oa.hleast.xswl.utils.ShortUrlUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import oa.hleast.xswl.service.venue.VenueService;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: 302
 * @date: 2021-07-05 15:14:29
 * @description: 场馆预约接口实现
 */
@Slf4j
@Service
@Transactional
@AllArgsConstructor
public class VenueServiceImpl implements VenueService {

    private final CmsVenueMainService mainService;
    private final CmsVenueScheduleService scheduleService;
    private final CmsVenueSignInService signInService;
    private final IUmsUserService umsUserService;
    private final AppProps appProps;

    /**
     * 添加场馆
     */
    @Override
    public boolean saveScheduleList(VenueScheduleAddForm form) {
        Long venueMainId = form.getVenueMainId();
        CmsVenueMain venueMain = mainService.getById(venueMainId);
        if (ObjectUtils.isEmpty(venueMain)) {
            throw new BizException("场馆不存在");
        }

        //清空以前的
        log.info("------------------------");
        scheduleService.remove(new LambdaQueryWrapper<com.hleast.sms.pojo.domain.CmsVenueSchedule>().eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getVenueMainId, venueMainId));


        Date startDate = form.getStartDate();
        Date endDate = form.getEndDate();
        List<Integer> weekList = form.getWeekList();
        List<Date> meetDayOfWeekDates = DateUtils.getMeetDayOfWeekDates(startDate, endDate, weekList);

        Integer amOpen = form.getAmOpen();
        Integer pmOpen = form.getPmOpen();

        Integer signUpType = venueMain.getSignUpType();
        List<com.hleast.sms.pojo.domain.CmsVenueSchedule> venueScheduleList = meetDayOfWeekDates.stream().map(date -> {
                    System.out.println(DateUtils.format(date, "yyyyMMdd hh:mm:ss"));
                    int year = DateUtils.year(date);
                    int month = DateUtils.month(date);
                    int day = DateUtils.dayOfMonth(date);

                    return com.hleast.sms.pojo.domain.CmsVenueSchedule.builder()
                            .venueMainId(venueMainId)
                            .venueName(venueMain.getVenueName())
                            .signUpType(signUpType)
                            .year(year)
                            .month(month + 1)
                            .day(day)
                            .amOpen(amOpen)
                            .pmOpen(pmOpen)
                            // TODO 硬编码后面改    8:30~11:30 12:30~17:30
                            .amStartDate(DateUtils.createCalendar(year, month, day, 8, 30, 0))
                            .amEndDate(DateUtils.createCalendar(year, month, day, 11, 30, 0))
                            .pmStartDate(DateUtils.createCalendar(year, month, day, 12, 30, 0))
                            .pmEndDate(DateUtils.createCalendar(year, month, day, 17, 30, 0))
                            .build();
                }
        ).collect(Collectors.toList());


        return scheduleService.saveBatch(venueScheduleList);
    }

    @Override
    public boolean saveSchedule(VenueScheduleAddForm form) {
        log.info("form = {}", form);
        Long venueMainId = form.getVenueMainId();
        CmsVenueMain venueMain = mainService.getById(venueMainId);
        if (ObjectUtils.isEmpty(venueMain)) {
            throw new BizException("场馆不存在");
        }

        Date startDate = form.getStartDate();
        Date endDate = form.getEndDate();
        int year = DateUtils.year(startDate);
        int month = DateUtils.month(startDate) + 1;
        int day = DateUtils.dayOfMonth(startDate);

        log.info("year = {} ,month = {} ,day = {}", year, month, day);

        // 检测是否已存在,存在进行更新
        List<com.hleast.sms.pojo.domain.CmsVenueSchedule> oldList = scheduleService.list(new LambdaQueryWrapper<com.hleast.sms.pojo.domain.CmsVenueSchedule>()
                .eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getVenueMainId, form.getVenueMainId())
                .eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getYear, year)
                .eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getMonth, month)
                .eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getDay, day)
        );

        log.info("oldList = {}", oldList);
        if (ObjectUtils.isNotEmpty(oldList) && ObjectUtils.isNotEmpty(oldList.get(0))) {
            log.info("update ,id = {}", oldList.get(0).getId());
            return scheduleService.update(new LambdaUpdateWrapper<com.hleast.sms.pojo.domain.CmsVenueSchedule>()
                    .set(com.hleast.sms.pojo.domain.CmsVenueSchedule::getAmOpen, form.getAmOpen())
                    .set(com.hleast.sms.pojo.domain.CmsVenueSchedule::getPmOpen, form.getPmOpen())
                    // TODO 硬编码后面改    8:30~11:30 12:30~17:30
                    .set(com.hleast.sms.pojo.domain.CmsVenueSchedule::getAmStartDate, DateUtils.createCalendar(year, month - 1, day, 8, 30, 0))
                    .set(com.hleast.sms.pojo.domain.CmsVenueSchedule::getAmEndDate, DateUtils.createCalendar(year, month - 1, day, 11, 30, 0))
                    .set(com.hleast.sms.pojo.domain.CmsVenueSchedule::getPmStartDate, DateUtils.createCalendar(year, month - 1, day, 12, 30, 0))
                    .set(com.hleast.sms.pojo.domain.CmsVenueSchedule::getPmEndDate, DateUtils.createCalendar(year, month - 1, day, 17, 30, 0))
                    .eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getId, oldList.get(0).getId())
            );
        }

        com.hleast.sms.pojo.domain.CmsVenueSchedule schedule = com.hleast.sms.pojo.domain.CmsVenueSchedule.builder()
                .venueMainId(venueMainId)
                .venueName(venueMain.getVenueName())
                .signUpType(venueMain.getSignUpType())
                .year(year)
                .month(month)
                .day(day)
                .amOpen(form.getAmOpen())
                .pmOpen(form.getPmOpen())
                // TODO 硬编码后面改    8:30~11:30 12:30~17:30
                .amStartDate(DateUtils.createCalendar(year, month - 1, day, 8, 30, 0))
                .amEndDate(DateUtils.createCalendar(year, month - 1, day, 11, 30, 0))
                .pmStartDate(DateUtils.createCalendar(year, month - 1, day, 12, 30, 0))
                .pmEndDate(DateUtils.createCalendar(year, month - 1, day, 17, 30, 0))
                .build();

        log.info("save schedule = {}", schedule);
        return scheduleService.save(schedule);
    }

    /**
     * 预约详情
     */
    @Override
    public CmsVenueSignInVo signInDetail(Long id) {
        CmsVenueSignIn venueSignIn = signInService.getById(id);
        CmsVenueSignInVo vo = new CmsVenueSignInVo();
        BeanUtil.copyProperties(venueSignIn, vo);
        Long userId = venueSignIn.getUserId();
        UmsUserDto userDto = umsUserService.getUserDtoById(userId);
        if (ObjectUtils.isEmpty(userDto) || ObjectUtils.isEmpty(userDto.getId())) {
            new BizException("预约用户信息异常");
        }
        vo.setIdCard(userDto.getIdCardNum());

        return vo;
    }

    /**
     * 场馆预约数据展示
     *
     * @param signInId 预约ID
     */
    @Override
    public HttpShowVenueSignInVo httpShowVenueSign(Long signInId) {
        CmsVenueSignIn venueSignIn = signInService.getById(signInId);
        Long scheduleId = venueSignIn.getScheduleId();
        com.hleast.sms.pojo.domain.CmsVenueSchedule schedule = scheduleService.getById(scheduleId);
        Long venueMainId = schedule.getVenueMainId();
        CmsVenueMain venueMain = mainService.getById(venueMainId);
        Long userId = venueSignIn.getUserId();
        UmsUserDto userDtoResult = umsUserService.getUserDtoById(userId);
        String idCardNumber = "";
        if (ObjectUtils.isNotEmpty(userDtoResult) && ObjectUtils.isNotEmpty(userDtoResult.getId())) {
            idCardNumber = userDtoResult.getIdCardNum();
        }

        HttpShowVenueSignInVo vo = HttpShowVenueSignInVo.builder()
                .venueName(venueMain.getVenueName())
                .mainImageUrl(venueMain.getMainImageUrl())
                .amOrPm(venueSignIn.getAmOrPm())
                .scheduleDate(String.format("%d年%d月%d日 %s", schedule.getYear(), schedule.getMonth(), schedule.getDay(), venueSignIn.getAmOrPm() == 0 ? "上午" : "下午"))
                .reserveUserName(venueSignIn.getReserveUserName())
                .reserveCompanyName(venueSignIn.getReserveCompanyName())
                .phone(venueSignIn.getPhone())
                .reserveNumber(venueSignIn.getReserveNumber())
                .idCardNumber(idCardNumber)
                .build();
        return vo;
    }

    /**
     * 管理端验签展示列表 未签到过的
     */
    @Override
    public List<ManagerSignInVenuesVo> listByUserIdForManager(Long userId, Long managerUserId) {
        log.info("管理端验签展示列表 start, userId = {},managerUserId = {}", userId, managerUserId);
        List<ManagerSignInVenuesVo> list = mainService.listByUserIdForManager(userId, managerUserId, 0, ReviewStatusEnum.PASS.getCode());

        log.info("管理端验签展示列表 start, list = {}", list);
        return list;
    }

    /**
     * 管理员验签
     *
     * @param venueSignInIdList 报名表ID
     * @param managerUserId     管理员ID
     * @return 是否成功
     */
    @Override
    public boolean managerUser2SignIn4User(List<Long> venueSignInIdList, Long managerUserId) {
        if (CollectionUtils.isEmpty(venueSignInIdList)) {
            return false;
        }

        boolean flag = true;
        for (Long venuesSignInId : venueSignInIdList) {
            CmsVenueSignIn venueSignIn = signInService.getById(venuesSignInId);
            if (ObjectUtils.isEmpty(venueSignIn)) {
                throw new BizException("该预约已失效,请联系场馆管理员");
            }
            venueSignIn.setSignInStatus(1);
            venueSignIn.setSignInDate(new Date());
            venueSignIn.setSignInType(1);
            venueSignIn.setSignInUserId(managerUserId);
            flag = flag && signInService.updateById(venueSignIn);
        }

        return flag;
    }

    @Override
    public Page<VenueUserSignInVo> pageUserSign(Integer page, Integer limit, Long venuesMainId) {
        LambdaQueryWrapper<CmsVenueSignIn> queryWrapper = new LambdaQueryWrapper<CmsVenueSignIn>()
                .eq(CmsVenueSignIn::getVenueMainId, venuesMainId)
                .orderByDesc(CmsVenueSignIn::getGmtModified)
                .orderByDesc(CmsVenueSignIn::getGmtCreate);

        Page<CmsVenueSignIn> cmsVenueSignInPage = signInService.page(new Page<>(page, limit), queryWrapper);
        if (cmsVenueSignInPage.getTotal() == 0) {
            return new Page<>(page, limit);
        }
        List<VenueUserSignInVo> cmsPlaceInfoList = cmsVenueSignInPage.getRecords().stream().map(pojo -> {
            VenueUserSignInVo signInVo = new VenueUserSignInVo();
            BeanUtil.copyProperties(pojo, signInVo);

            log.info("userId = {}", pojo.getUserId());

            UmsUserDto userDto = umsUserService.getUserDtoById(pojo.getUserId());
            signInVo.setFullUserName(userDto.getFullname());
            signInVo.setMobile(userDto.getMobile());
            signInVo.setIdCard(userDto.getIdCardNum());

            return signInVo;
        }).collect(Collectors.toList());

        Page<VenueUserSignInVo> voPage = new Page<>(page, limit, cmsVenueSignInPage.getTotal());
        voPage.setRecords(cmsPlaceInfoList);

        return voPage;
    }

    @Override
    public List<CmsVenueScheduleVo> findScheduleByYearAndMonth(Integer year, Integer month, Long venueMainId) {
        List<com.hleast.sms.pojo.domain.CmsVenueSchedule> list = scheduleService.list(new LambdaQueryWrapper<com.hleast.sms.pojo.domain.CmsVenueSchedule>()
                .eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getVenueMainId, venueMainId)
                .eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getYear, year)
                .eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getMonth, month)
        );
        List<CmsVenueScheduleVo> voList = list.stream().map(pojo -> {
            CmsVenueScheduleVo vo = new CmsVenueScheduleVo();
            BeanUtil.copyProperties(pojo, vo);
            vo.setAmSignIn(isReserved(pojo.getId(), 0, ReviewStatusEnum.PASS.getCode()) && 1 == pojo.getSignUpType() ? 1 : 0);
            vo.setPmSignIn(isReserved(pojo.getId(), 1, ReviewStatusEnum.PASS.getCode()) && 1 == pojo.getSignUpType() ? 1 : 0);
            return vo;
        }).collect(Collectors.toList());
        log.info("voList = {}", voList);
        return voList;
    }

    @Override
    public List<ShowVenueScheduleVo> findSchedule30DayFromNow(Long venueMainId) {
        Date nowDate = new Date();
        Date startDate = DateUtils.beginOfDay(nowDate).toJdkDate();
        Date endDate = getAfter30Day(nowDate);
        List<com.hleast.sms.pojo.domain.CmsVenueSchedule> list = scheduleService.list(new LambdaQueryWrapper<com.hleast.sms.pojo.domain.CmsVenueSchedule>()
                .ge(com.hleast.sms.pojo.domain.CmsVenueSchedule::getAmStartDate, startDate)
                .le(com.hleast.sms.pojo.domain.CmsVenueSchedule::getPmEndDate, endDate)
                .eq(com.hleast.sms.pojo.domain.CmsVenueSchedule::getVenueMainId, venueMainId)
                .orderByAsc(com.hleast.sms.pojo.domain.CmsVenueSchedule::getYear)
                .orderByAsc(com.hleast.sms.pojo.domain.CmsVenueSchedule::getMonth)
                .orderByAsc(com.hleast.sms.pojo.domain.CmsVenueSchedule::getDay)
        );

        List<ShowVenueScheduleVo> voList = new ArrayList<>();
        for (com.hleast.sms.pojo.domain.CmsVenueSchedule schedule : list) {
            ShowVenueScheduleVo vo = new ShowVenueScheduleVo();
            BeanUtil.copyProperties(schedule, vo);
            vo.setScheduleDate(DateUtils.format(DateUtils.createCalendar(schedule.getYear(), schedule.getMonth() - 1, schedule.getDay(), 0, 0, 0), "yyyy-MM-dd"));

            ShowDayVenueScheduleVo amDayVo = ShowDayVenueScheduleVo.builder()
                    .amOrPm(0)
                    .startDate(schedule.getAmStartDate())
                    .endDate(schedule.getAmEndDate())
                    .open(schedule.getAmOpen())
                    .reserve(isReserved(schedule.getId(), 0, ReviewStatusEnum.PASS.getCode()) && 1 == schedule.getSignUpType() ? 1 : 0)
                    .build();

            ShowDayVenueScheduleVo pmDayVo = ShowDayVenueScheduleVo.builder()
                    .amOrPm(1)
                    .startDate(schedule.getPmStartDate())
                    .endDate(schedule.getPmEndDate())
                    .open(schedule.getPmOpen())
                    .reserve(isReserved(schedule.getId(), 1, ReviewStatusEnum.PASS.getCode()) && 1 == schedule.getSignUpType() ? 1 : 0)
                    .build();
            ArrayList<ShowDayVenueScheduleVo> dayVoList = new ArrayList<>();
            dayVoList.add(amDayVo);
            dayVoList.add(pmDayVo);

            vo.setDayVoList(dayVoList);

            voList.add(vo);
        }

        log.info("voList = {}", voList);
        return voList;
    }

    /**
     * 场地预约
     */
    @Override
    public boolean venueReserve(Long userId, CmsVenueSignInForm form) {

        Long venueMainId = form.getVenueMainId();
        CmsVenueMain venueMain = mainService.getById(venueMainId);
        Long scheduleId = form.getScheduleId();
        com.hleast.sms.pojo.domain.CmsVenueSchedule venueSchedule = scheduleService.getById(scheduleId);
        Integer amOrPm = form.getAmOrPm();
        //检查该场地是否已经被预约并且审核通过
        if (1 == venueMain.getSignUpType() && isReserved(scheduleId, amOrPm, ReviewStatusEnum.PASS.getCode())) {
            throw new BizException("该场地已经被预约过了,哦!");
        }
        // 检查自己是否预约过
        if (isReservedByUser(userId, scheduleId, amOrPm, ReviewStatusEnum.UN_KNOWN.getCode())) {
            throw new BizException("您已经预约过该场馆了,哦!");
        }

        // 校验人数
        // MaxNumber 实际为最少预约人数 预约人数下限
        if (1 == venueMain.getSignUpType()) {
            if (form.getReserveNumber() < venueMain.getMaxNumber()) {
                throw new BizException(String.format("该场馆至少需要%d", venueMain.getMaxNumber()));
            }
        }

        //新增预约
        CmsVenueSignIn cmsVenueSignIn = new CmsVenueSignIn();
        BeanUtil.copyProperties(form, cmsVenueSignIn);
        if (0 == form.getAmOrPm()) {
            cmsVenueSignIn.setScheduleStartDate(venueSchedule.getAmStartDate());
            cmsVenueSignIn.setScheduleEndDate(venueSchedule.getAmEndDate());
        } else if (1 == form.getAmOrPm()) {
            cmsVenueSignIn.setScheduleStartDate(venueSchedule.getPmStartDate());
            cmsVenueSignIn.setScheduleEndDate(venueSchedule.getPmEndDate());
        } else {
            throw new BizException("系统异常");
        }
        cmsVenueSignIn.setUserId(userId);
        boolean save = signInService.save(cmsVenueSignIn);

        return save;
    }


    /**
     * 我的场地预约
     */
    @Override
    public List<MyVenueReserveVo> findMyReserve(Long userId) {
        List<CmsVenueSignIn> pojoList = signInService.list(new LambdaQueryWrapper<CmsVenueSignIn>()
                .eq(CmsVenueSignIn::getUserId, userId)
                .orderByAsc(CmsVenueSignIn::getGmtCreate)
                .orderByAsc(CmsVenueSignIn::getGmtModified)
        );
        List<MyVenueReserveVo> voList = pojoList.stream().map(pojo -> {
            CmsVenueMain cmsVenueMain = mainService.getById(pojo.getVenueMainId());
            MyVenueReserveVo vo = new MyVenueReserveVo();
            BeanUtil.copyProperties(cmsVenueMain, vo);
            vo.setSignInId(pojo.getId());
            vo.setReviewStatus(pojo.getReviewStatus());
            vo.setReviewRemark(ObjectUtils.isNotEmpty(pojo.getReviewRemark()) ? pojo.getReviewRemark() : "");
            vo.setScheduleDate(String.format("%s%s", DateUtils.format(pojo.getScheduleStartDate(), "MM月dd日"), 0 == pojo.getAmOrPm() ? "上午" : "下午"));
            vo.setSignInDate(pojo.getSignInDate());
            vo.setSignInStatus(pojo.getSignInStatus());
            return vo;
        }).collect(Collectors.toList());
        log.info("voList = {}", voList);
        return voList;
    }

    /**
     * 场地签到
     */
    @Override
    public boolean reserveSignIn(Long signInId) {
        Long userId = RequestUtils.getUserId();
        CmsVenueSignIn venueSignIn = signInService.getById(signInId);
        if (ObjectUtils.isEmpty(venueSignIn)) {
            throw new BizException("该预约已失效,请联系场馆管理员");
        }
        venueSignIn.setSignInStatus(1);
        venueSignIn.setSignInDate(new Date());
        venueSignIn.setSignInType(0);
        venueSignIn.setSignInUserId(userId);
        boolean update = signInService.updateById(venueSignIn);
        return update;
    }


    /**
     * 场馆是否已被预约
     */
    private boolean isReserved(Long scheduleId, Integer amOrPm, Integer reviewStatus) {
        int count = signInService.count(new LambdaQueryWrapper<CmsVenueSignIn>()
                .eq(CmsVenueSignIn::getScheduleId, scheduleId)
                .eq(CmsVenueSignIn::getAmOrPm, amOrPm)
                .eq(CmsVenueSignIn::getReviewStatus, reviewStatus)
        );

        if (count > 0) {
            return true;
        } else {
            return false;
        }

    }

    /**
     * 场馆是否已被某用户预约
     */
    private boolean isReservedByUser(Long userId, Long scheduleId, Integer amOrPm, Integer reviewStatus) {
        int count = signInService.count(new LambdaQueryWrapper<CmsVenueSignIn>()
                .eq(CmsVenueSignIn::getScheduleId, scheduleId)
                .eq(CmsVenueSignIn::getAmOrPm, amOrPm)
                .eq(CmsVenueSignIn::getReviewStatus, reviewStatus)
                .eq(CmsVenueSignIn::getUserId, userId)
        );

        if (count > 0) {
            return true;
        } else {
            return false;
        }
    }

    private Date getAfter30Day(Date nowDate) {

        DateTime afterDate = DateUtil.offsetDay(nowDate, 30);
        DateTime beginOfDay = DateUtils.endOfDay(afterDate.toJdkDate());

        return beginOfDay.toJdkDate();
    }
}

